package trabalho.das.fxml.views.uicomponents;

import entidades.Ancora;
import entidades.Cor;
import entidades.DesenhoDinamico;
import entidades.Rectangulo;
import entidades.TelhaBase;
import entidades.Textura;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javafx.beans.binding.Bindings;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;

/**
 * Trabalho DAS - 2014/2015
 *
 * @author a21210380, a21190325, a21210392
 */
public class TelhaGrafica extends Rectangle {

    private final TelhaBase telha;
    private final List<MyAncora> myAncoras;
    private final Pane pane;
    private ImageView image;
    private boolean isBind;

    private final ListChangeListener<Ancora> listChangeListener;
    private final ChangeListener<Cor> changeListenerCor;
    private final ChangeListener<Textura> changeListenerFundo;

    public TelhaGrafica(Pane pane, TelhaBase telha) {
        this(pane, telha, telha.getRectangulo());
    }

    public TelhaGrafica(Pane pane, TelhaBase telha, Rectangulo rectangulo) {
        super(
                rectangulo.getOrigem().getX(),
                rectangulo.getOrigem().getY(),
                rectangulo.getLargura(),
                rectangulo.getAltura());
        this.myAncoras = new ArrayList<>(telha.getAncoras().size());
        ObservableList<Ancora> tempAncoras = telha.getAncoras();
        tempAncoras.forEach((Ancora c) -> {
            MyAncora myAncora = new MyAncora(c);
            myAncoras.add(myAncora);
        });

        this.telha = telha;
        this.isBind = false;
        this.pane = pane;

        listChangeListener = (ListChangeListener.Change<? extends Ancora> c) -> {
            while (c.next()) {
                if (c.wasAdded()) {
                    c.getAddedSubList().stream().forEach((ancora) -> {
                        MyAncora myAncora = new MyAncora(ancora).bind();
                        myAncoras.add(myAncora);
                        pane.getChildren().add(myAncora);
                    });
                }
            }
        };

        changeListenerCor = (ObservableValue<? extends Cor> observable, Cor oldValue, Cor newValue) -> {
            setCorLinha();
        };

        changeListenerFundo = (ObservableValue<? extends Textura> observable, Textura oldValue, Textura newValue) -> {
            setTextura();
        };
        
        setFill(Color.TRANSPARENT);
        setTextura();
        setCorLinha();
    }

    public final TelhaGrafica bind() {
        if (isBind) {
            return this;
        }

        // Bind da cor do estilo
        telha.corLinhaProperty().addListener(changeListenerCor);
        telha.fundoProperty().addListener(changeListenerFundo);

        // Bind do rectangulo e adição do mesmo
        Rectangulo rectangulo = telha.getRectangulo();
        Bindings.bindBidirectional(xProperty(), rectangulo.getOrigem().xProperty());
        Bindings.bindBidirectional(yProperty(), rectangulo.getOrigem().yProperty());
        Bindings.bindBidirectional(widthProperty(), rectangulo.larguraProperty());
        Bindings.bindBidirectional(heightProperty(), rectangulo.alturaProperty());
        pane.getChildren().add(this);

        // Bind da image e adição da mesma se existir
        if (image != null) {
            Bindings.bindBidirectional(image.xProperty(), rectangulo.getOrigem().xProperty());
            Bindings.bindBidirectional(image.yProperty(), rectangulo.getOrigem().yProperty());
            Bindings.bindBidirectional(image.fitWidthProperty(), rectangulo.larguraProperty());
            Bindings.bindBidirectional(image.fitHeightProperty(), rectangulo.alturaProperty());
            pane.getChildren().add(image);
        }

        // Bind das ancoras e adição da mesma se existir
        myAncoras.forEach((MyAncora c) -> {
            pane.getChildren().add(c.bind());
        });

        // Bind do listener das ancoras
        telha.getAncoras().addListener(listChangeListener);

        this.isBind = true;
        return this;
    }

    public final TelhaGrafica unbind() {
        if (!isBind) {
            return this;
        }

        // Unbind da cor do estilo
        telha.corLinhaProperty().removeListener(changeListenerCor);
        telha.fundoProperty().removeListener(changeListenerFundo);

        // Unbind do rectangulo e remoção do mesmo
        Rectangulo rectangulo = telha.getRectangulo();
        Bindings.unbindBidirectional(xProperty(), rectangulo.getOrigem().xProperty());
        Bindings.unbindBidirectional(yProperty(), rectangulo.getOrigem().yProperty());
        Bindings.unbindBidirectional(widthProperty(), rectangulo.larguraProperty());
        Bindings.unbindBidirectional(heightProperty(), rectangulo.alturaProperty());
        pane.getChildren().remove(this);

        // Unbind da image e remoção da mesma se existir
        if (image != null) {
            Bindings.unbindBidirectional(image.xProperty(), rectangulo.getOrigem().xProperty());
            Bindings.unbindBidirectional(image.yProperty(), rectangulo.getOrigem().yProperty());
            Bindings.unbindBidirectional(image.fitWidthProperty(), rectangulo.larguraProperty());
            Bindings.unbindBidirectional(image.fitHeightProperty(), rectangulo.alturaProperty());
            pane.getChildren().remove(image);
        }

        // Unbind das ancoras e remoção da mesma se existir
        myAncoras.forEach((MyAncora c) -> {
            pane.getChildren().remove(c.unbind());
        });

        // Unbind do listener das ancoras
        telha.getAncoras().removeListener(listChangeListener);

        isBind = false;
        return this;
    }

    public List<MyAncora> getAncoras() {
        return myAncoras;
    }

    public ImageView getImage() {
        return image;
    }

    private void setCorLinha() {
        Cor corDaLinha = telha.getCorDaLinha();
        setStroke(Color.color(corDaLinha.getRed(), corDaLinha.getGreen(), corDaLinha.getBlue(), corDaLinha.getOpacity()));
    }
    
    private void setTextura() {
        Textura fundo = telha.getFundo();
        if (fundo == null || fundo.getCaminhoDoFicheiro().equals(Textura.NO_TEXTURE)) {
            DesenhoDinamico desenho = telha.getDesenho();
            if (desenho != null) {
                setImageViewImage(new File(desenho.getCaminhoDoFicheiro()));
            }
        } else {
            setImageViewImage(new File(fundo.getCaminhoDoFicheiro()));
        }
    }

    private void setImageViewImage(File f) {
        if (f.exists()) {
            try (InputStream is = new FileInputStream(f)) {
                if(image != null) {
                    image.setImage(new Image(is));
                } else {
                    image = new ImageView(new Image(is));
                }
            } catch (IOException ex) {
            }
        }
    }
}
